package au.com.acpfg.xml.query; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import javax.swing.JComboBox; /** * Displays a row in the configure dialog which represents a single XQuery and how the results are * to be displayed to the user * * @author andrew.cassin * */ public class XMLQueryEntry { /** * Any change to this enum must be reflected in the methods below so that the configure dialog and user interaction * is correct. Be careful, since any change must be backwards compatible with existing workflows as the queries are saved! * @author andrew.cassin * */ public enum ResultsType { RAW_XML, // XQuery result fragment (string cell) with one row per result per query per file RAW_XML_COLLECTION, // KNIME LIST cell with all results in the collection for a given XQuery for a given file TEXT, // xml with tags removed TEXT_COLLECTION, // each element in results as a separation list item (KNIME collection cell) XMLATTR_COLLECTION, // list of attributes in the selected element(s) (KNIME collection cell) RESULTS_COUNT, // number of matching elements ELEMENTS_AS_COLUMNS,// direct childen elements within the matching element are turned into KNIME columns (may add many columns!) ELEMENT_DISTRIBUTION // how many times each element occurs within the hit // more to come in future... }; private String m_query; // multi-line supported private String m_name; // name for the query (human assigned) private ResultsType[] m_types; // what results are expected for the query private boolean m_fail_if_empty; // fail parsing if no records match query (0 hits) private boolean m_disabled; // do not execute() query if true, but show in list public XMLQueryEntry(String name, String xquery, ResultsType[] rt, boolean fail_if_empty) { m_name = name; m_query = xquery; m_types = rt; m_fail_if_empty = fail_if_empty; m_disabled = false; } public XMLQueryEntry(String name, String xquery, ResultsType result_format, boolean fail_if_empty) { this(name, xquery, new ResultsType[] { result_format }, fail_if_empty); } public XMLQueryEntry(String name, String xquery, ResultsType result_format) { this(name, xquery, result_format, true); } public XMLQueryEntry(String name, String xquery) { this(name, xquery, ResultsType.RAW_XML); } public XMLQueryEntry(int n) { this("Query"+n, "//some/element"); } /** * Returns the list of combobox items in the same order as defined in enum ResultTypes * * @return guaranteed non-null */ public static String[] rt2items() { ResultsType[] vals = ResultsType.values(); String[] vec = new String[vals.length]; int idx = 0; for (ResultsType rt : vals) { vec[idx++] = colname(rt); } return vec; } /** * Returns the enum value which corresponds to the combobox item in the supplied parameter * * @param selectedItem the english text which appears in the combobox * @return */ public static ResultsType[] item2rt(Object[] selectedItems) { HashSet<ResultsType> wanted = new HashSet<ResultsType>(); for (Object o : selectedItems) { String s = o.toString(); if (s.equals("XQuery Result")) { wanted.add(ResultsType.RAW_XML); } else if (s.equals("XQuery Result (list)")) { wanted.add(ResultsType.RAW_XML_COLLECTION); } else if (s.equals("Result Count")) { wanted.add(ResultsType.RESULTS_COUNT); } else if (s.equals("Text only")) { wanted.add(ResultsType.TEXT); } else if (s.equals("Text only (list)")) { wanted.add(ResultsType.TEXT_COLLECTION); } else if (s.startsWith("Elements as columns")) { wanted.add(ResultsType.ELEMENTS_AS_COLUMNS); } else if (s.startsWith("Element Distribution")) { wanted.add(ResultsType.ELEMENT_DISTRIBUTION); } else { wanted.add(ResultsType.XMLATTR_COLLECTION); } } return wanted.toArray(new ResultsType[0]); } /** * Returns the index (combobox list position, zero relative) which corresponds to the specified * <code>enum ResultsType</code> value * * @param t_result_types * @param results * @return */ protected static int[] rt2idx(ResultsType[] wanted_results) { HashSet<Integer> vec = new HashSet<Integer>(); // NB: this return value must correspond to the defined <code>enum ResultsType</code> value order for (ResultsType rt : wanted_results) { if (rt == ResultsType.RESULTS_COUNT) { vec.add(new Integer(5)); } else if (rt == ResultsType.TEXT) { vec.add(new Integer(2)); } else if (rt == ResultsType.TEXT_COLLECTION) { vec.add(new Integer(3)); } else if (rt == ResultsType.XMLATTR_COLLECTION) { vec.add(new Integer(4)); } else if (rt == ResultsType.RAW_XML_COLLECTION) { vec.add(new Integer(1)); } else if (rt == ResultsType.ELEMENTS_AS_COLUMNS) { vec.add(new Integer(6)); } else if (rt == ResultsType.ELEMENT_DISTRIBUTION) { vec.add(new Integer(7)); } else { vec.add(new Integer(0)); } } int[] ret = new int[vec.size()]; int idx = 0; for (Integer i : vec) { ret[idx++] = i.intValue(); } return ret; } /** * Return the name for the column (which corresponds to the combobox entry in the configure dialog) * which corresponds to the specified <code>enum ResultsType</code> * * @param rt * @return null is returned for unknown enum values (should not happen in normal circumstances) */ public static String colname(ResultsType rt) { if (rt == ResultsType.RAW_XML) { return "XQuery Result"; } else if (rt == ResultsType.ELEMENTS_AS_COLUMNS) { return "Elements as columns"; // column names are the XML names of the elements in the result so this wont appear in the output table } else if (rt == ResultsType.RAW_XML_COLLECTION) { return "XQuery Result (list)"; } else if (rt == ResultsType.RESULTS_COUNT) { return "Result Count"; } else if (rt == ResultsType.TEXT) { return "Text only"; } else if (rt == ResultsType.TEXT_COLLECTION) { return "Text only (list)"; } else if (rt == ResultsType.XMLATTR_COLLECTION) { return "Attributes (list)"; } else if (rt == ResultsType.ELEMENT_DISTRIBUTION) { return "Element Distribution"; } return null; } /** * constructor which reverse the serialisation performed by toString() * @param xqe_serialised */ public XMLQueryEntry(String xqe_serialised) { String[] lines = xqe_serialised.split("\n"); StringBuffer sb = new StringBuffer(); for (int i=4; i<lines.length; i++) { sb.append(lines[i]); sb.append("\n"); } m_name = lines[0]; m_types = map2results(lines[1].split(",")); m_fail_if_empty = lines[2].equals("true"); m_disabled = lines[3].equals("true"); m_query = sb.toString(); } private static ResultsType[] map2results(String[] wanted_list) { HashSet<ResultsType> vec = new HashSet<ResultsType>(); for (String s : wanted_list) { if (s.equals("RAW_XML")) vec.add(ResultsType.RAW_XML); else if (s.equals("RAW_XML_COLLECTION")) vec.add(ResultsType.RAW_XML_COLLECTION); else if (s.equals("TEXT")) vec.add(ResultsType.TEXT); else if (s.equals("TEXT_COLLECTION")) vec.add(ResultsType.TEXT_COLLECTION); else if (s.equals("XMLATTR_COLLECTION")) vec.add(ResultsType.XMLATTR_COLLECTION); else if (s.equals("ELEMENTS_AS_COLUMNS")) vec.add(ResultsType.ELEMENTS_AS_COLUMNS); else if (s.equals("ELEMENT_DISTRIBUTION")) vec.add(ResultsType.ELEMENT_DISTRIBUTION); else vec.add(ResultsType.RESULTS_COUNT); } return vec.toArray(new ResultsType[0]); } public String getName() { return m_name; } public void setName(String new_name) { m_name = new_name; } public String getQuery() { return m_query; } public void setQuery(String new_query) { m_query = new_query; } public ResultsType[] getWantedResults() { return m_types; } public Set<ResultsType> getWantedResultsSet() { HashSet<ResultsType> ret = new HashSet<ResultsType>(); for (ResultsType rt : m_types) { ret.add(rt); } return ret; } public void setResults(ResultsType[] new_results) { m_types = new_results; } @Override public String toString() { ResultsType[] wanted = getWantedResults(); StringBuffer sb = new StringBuffer(1024); int idx = 0; for (ResultsType w : wanted) { sb.append(w.toString()); if (idx++ < wanted.length-1) sb.append(','); // NB: must correspond to de-serialisation constructor (see above) } return getName()+"\n"+sb.toString()+"\n"+m_fail_if_empty+"\n"+m_disabled+"\n"+getQuery(); } public boolean isEnabled() { return !m_disabled; } public void setEnabled(boolean new_val) { m_disabled = !new_val; } public boolean getFailEmpty() { return m_fail_if_empty; } /** * Set to true if the node should abort execution if the XQuery does not match anything (may indicate * XML format has changed) * @param new_fail if set to true, node will abort if the XQuery matches nothing in the current document */ public void setFailEmpty(boolean new_fail) { m_fail_if_empty = new_fail; } }